﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace PixelWarfare
{
    /// <summary>
    /// Creates a ring that grows and shrinks based on the diameter you call draw with.
    /// Optimized so if you continue to pass the same diameter no extra calculations will be done.
    /// </summary>
    class RadarCircle
    {
        #region Fields

        private int x, y;

        private Bitmap bmp;
        private Color color;

        private int currentDiameter;
        private int oldDiameter;        // Used to recolor old pixels back to black.
        private int thickness;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new Radar Circle with the given parameters.
        /// </summary>
        /// <param name="x">X coordinate of the center of the circle.</param>
        /// <param name="y">Y coordinate of the center of the circle.</param>
        /// <param name="maxDiameter">The maximum diameter the circle will ever reach.</param>
        /// <param name="color">The color of the circle.</param>
        /// <param name="thickness">Optional thickness of the outer ring.</param>
        public RadarCircle(int x, int y, int maxDiameter, Color color, int thickness = 5)
        {
            this.x = x;
            this.y = y;

            // Make it a bit bigger then the max diameter so the glow from the rings isnt cut off.
            bmp = new Bitmap(maxDiameter + thickness * 2, maxDiameter + thickness * 2);
            
            this.thickness = thickness;
            this.color = color;

            oldDiameter = -1;
            currentDiameter = -1;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Draws the radar circle centered at its coordinates.
        /// </summary>
        /// <param name="g">Graphics to draw on.</param>
        public void Draw(Graphics g)
        {       
            g.DrawImage(bmp, x - bmp.Width / 2, y - bmp.Height / 2);
        }

        /// <summary>
        /// Update its diameter if he new diameter is different.
        /// Then recalculate the bitmap.
        /// </summary>
        /// <param name="newDiameter">The new diameter.</param>
        public void ChangeSize(int newDiameter)
        {
            if (newDiameter != currentDiameter)
            {
                oldDiameter = currentDiameter;
                currentDiameter = newDiameter;
                RecalculateBitmap();
                bmp.MakeTransparent(Color.Black);
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Recalculate where the ring is by the new diameter and
        /// color the pixels appropriately.
        /// </summary>
        private void RecalculateBitmap()
        {
            for (int y = 0; y < bmp.Height; y++)
            {
                for (int x = 0; x < bmp.Width; x++)
                {
                    double distance = DistanceToCentre(x, y);                    

                    // if the pixel is within thickness of the centre of the circle
                    if (Math.Abs(distance) < thickness)
                    {
                        /* If the pixel is at the centre meaning distance = 0
                         * Then take the full colour, ie. Darken(color, 1)
                         * otherwise if it is further away form the centre
                         * then Darken(color, 1 - (distance / thickness)). */
                        bmp.SetPixel(x, y, Utility.Darken(color,
                            1 - (Math.Abs(distance) / thickness)));
                    }
                    // If the pixel is within thickness of the radius of the circle.
                    else if (Math.Abs(distance - currentDiameter/2) < thickness)
                    {
                        
                        // Same as above except distance from radius, not centre.
                        bmp.SetPixel(x, y, Utility.Darken(color,
                            1 - (Math.Abs(distance - currentDiameter/2) / thickness)));
                    }

                    /* If the pixel was in the range of the old radius
                     * and its not in the new range set it back to black. */
                    else if (Math.Abs(distance - oldDiameter / 2) < thickness)
                    {
                        bmp.SetPixel(x, y, Color.Black);
                    }
                }
            }

            
        }

        /// <summary>
        /// Returns the distance to the centre of the circle from a given coordinate.
        /// </summary>
        /// <param name="x">X coordinate.</param>
        /// <param name="y">Y coordinate.</param>
        /// <returns>The distance from the point to the centre.</returns>
        private double DistanceToCentre(double x, double y)
        {
            return Math.Sqrt(Math.Pow(x - bmp.Width / 2, 2) + Math.Pow(y - bmp.Height / 2, 2));
        }

        #endregion
    }
}
